fix: exclude FP8 quantized weights from MXFP4 de-osc - #1081
Conversation
There was a problem hiding this comment.
🔵 Needs a closer look
It affects low-level quantization/training behavior via a custom FP4 dtype marker and should get final human validation in a real runtime/config matrix before approval.
Pull request overview
This PR refines MXFP4 weight de-oscillation eligibility detection so FP8-quantized modules are no longer mistakenly picked up when quantized_weight_buffer exists for both FP4 and FP8 paths (e.g., when gradient accumulation is enabled).
Changes:
- In Primus-Turbo FP4 paths with
get_num_microbatches() == 1, setquantized_weight_bufferto an empty tensor withfloat4_e2m1fn_x2dtype (instead oftorch.uint8) to act as an FP4-specific runtime marker. - In the weight de-osc eligibility scan, require
quantized_weight_buffer.dtype == float4_e2m1fn_x2to filter out FP8 buffers.
File summaries
| File | Description |
|---|---|
primus/backends/megatron/core/optimizer/weight_deosc.py |
Tightens FP4 eligibility detection by checking quantized_weight_buffer dtype. |
primus/backends/megatron/core/extensions/primus_turbo.py |
Makes the FP4 “marker” buffer use float4_e2m1fn_x2 dtype so de-osc can reliably identify FP4 modules. |
Review details
- Files reviewed: 2/2 changed files
- Comments generated: 0
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
There was a problem hiding this comment.
🟡 Changes recommended
There are concrete runtime-breaking issues (undefined weights variable in multiple FP4 paths) and the new dtype check can raise/disable eligibility when the MXFP4 dtype symbol is unavailable or the marker isn’t a tensor.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
Suppressed comments (3)
primus/backends/megatron/core/extensions/primus_turbo.py:1365
weightsis not defined in thisforward_internal(the local variable isweight), sodevice=weights.devicewill raiseNameErrorthe first time this FP4 path runs.
self.quantized_weight_buffer = torch.empty(
0, device=weights.device, dtype=float4_e2m1fn_x2
)
primus/backends/megatron/core/extensions/primus_turbo.py:1571
weightsis not defined in thisforward_internal(the local variable isweight), sodevice=weights.devicewill raiseNameErrorthe first time this FP4 path runs.
self.quantized_weight_buffer = torch.empty(
0, device=weights.device, dtype=float4_e2m1fn_x2
)
primus/backends/megatron/core/extensions/primus_turbo.py:1790
weightsis not defined in thisforward_internal(the local variable isweight), sodevice=weights.devicewill raiseNameErrorthe first time this FP4 path runs.
self.quantized_weight_buffer = torch.empty(
0, device=weights.device, dtype=float4_e2m1fn_x2
)
- Files reviewed: 2/2 changed files
- Comments generated: 2
- Review effort level: Lite
| quantized_weight_buffer = getattr(module, "quantized_weight_buffer", None) | ||
| if quantized_weight_buffer is None or quantized_weight_buffer.dtype != _float4_e2m1fn_x2: | ||
| continue |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
🔵 Needs a closer look
Several FP4 single-microbatch code paths reference weights.device where weights is undefined, which will raise a runtime NameError.
Review details
Suppressed comments (3)
primus/backends/megatron/core/extensions/primus_turbo.py:1571
weightsis not defined in this scope; usingweights.devicewill raise a NameError on the FP4 single-microbatch path. Useweight.devicehere.
self.quantized_weight_buffer = torch.empty(
0, device=weights.device, dtype=float4_e2m1fn_x2
)
primus/backends/megatron/core/extensions/primus_turbo.py:1790
weightsis not defined in this scope; usingweights.devicewill raise a NameError on the FP4 single-microbatch path. Useweight.devicehere.
self.quantized_weight_buffer = torch.empty(
0, device=weights.device, dtype=float4_e2m1fn_x2
)
primus/backends/megatron/core/extensions/primus_turbo.py:1365
weightsis not defined in this scope; usingweights.devicewill raise a NameError the first time this FP4 single-microbatch path runs. Use the localweightparameter's device instead.
self.quantized_weight_buffer = torch.empty(
0, device=weights.device, dtype=float4_e2m1fn_x2
)
- Files reviewed: 2/2 changed files
- Comments generated: 0 new
- Review effort level: Lite
There was a problem hiding this comment.
🔵 Needs a closer look
The new dtype-based eligibility check can raise AttributeError when quantized_weight_buffer is not a tensor-like object exposing .dtype, so it should be made defensive before merging.
Review details
Suppressed comments (1)
primus/backends/megatron/core/optimizer/weight_deosc.py:355
_build_eligible_idsnow unconditionally accessesquantized_weight_buffer.dtype. Some callers (including existing unit tests) use non-tensor markers forquantized_weight_buffer, which would raiseAttributeErrorhere. Consider reading dtype defensively (and update tests/markers to use the FP4 dtype marker).
match weights the forward actually quantizes.
"""
- Files reviewed: 2/2 changed files
- Comments generated: 0 new
- Review effort level: Lite
There was a problem hiding this comment.
🔵 Needs a closer look
The new dtype-based eligibility check can raise an AttributeError for non-tensor markers (and will break existing CPU unit tests) unless guarded.
Review details
Suppressed comments (1)
primus/backends/megatron/core/optimizer/weight_deosc.py:366
_build_eligible_idsnow assumesquantized_weight_bufferalways has a.dtypeattribute; if a module (or the CPU unit tests intests/unit_tests/backends/megatron/test_weight_deosc.py) uses a sentinel object to mark “FP4 forward ran”, this will raiseAttributeErrorat runtime. Usegetattr(..., "dtype", None)to keep the dtype filter without crashing on non-tensor sentinels.
quantized_weight_buffer = getattr(module, "quantized_weight_buffer", None)
if quantized_weight_buffer is None or quantized_weight_buffer.dtype != _float4_e2m1fn_x2:
- Files reviewed: 2/2 changed files
- Comments generated: 0 new
- Review effort level: Lite
What's wrong
MXFP4 weight-deosc process selects eligible weights by looking for
quantized_weight_buffer. FP4 and FP8 quantized modules both have this buffer when gradient accumulation enabled.What's changed
float4_e2m1fn_x2dtype when gradient accumulation disabled.quantized_weight_bufferin MXFP4 weight de-osc.@HuangWei-95